﻿using System;
using System.Text;
using System.Web.Mvc;
using System.Web.Routing;

namespace SnippetBox.Core.Common.Paging {
    public class Pager {
        private readonly ViewContext _viewContext;
        private readonly int _pageSize;
        private readonly int _currentPage;
        private readonly int _totalItemCount;
        private readonly RouteValueDictionary _linkWithoutPageValuesDictionary;

        public Pager(ViewContext viewContext, int pageSize, int currentPage, int totalItemCount,
                     RouteValueDictionary valuesDictionary) {
            _viewContext = viewContext;
            _pageSize = pageSize;
            _currentPage = currentPage;
            _totalItemCount = totalItemCount;
            _linkWithoutPageValuesDictionary = valuesDictionary;
        }

        public string RenderHtml() {
            const int NR_OF_PAGES_TO_DISPLAY = 5;
            int pageCount = (int)Math.Ceiling(_totalItemCount / (double)_pageSize);

            var sb = new StringBuilder();

            //Begin Ul tag
            sb.Append("<ul class=\"paging\">" + Environment.NewLine);

            // Previous
            if (_currentPage > 1) {
                sb.Append(GeneratePageLink("Previous", _currentPage - 1, true));
            }
            else {
                sb.Append("<li class=\"previous-off\">Previous</li>" + Environment.NewLine);
            }

            int start = 1;
            int end = pageCount;

            if (pageCount > NR_OF_PAGES_TO_DISPLAY) {
                int middle = (int)Math.Ceiling(NR_OF_PAGES_TO_DISPLAY / 2d) - 1;
                int below = (_currentPage - middle);
                int above = (_currentPage + middle);

                if (below < 4) {
                    above = NR_OF_PAGES_TO_DISPLAY;
                    below = 1;
                }
                else if (above > (pageCount - 4)) {
                    above = pageCount;
                    below = (pageCount - NR_OF_PAGES_TO_DISPLAY);
                }

                start = below;
                end = above;
            }

            if (start > 3) {
                sb.Append(GeneratePageLink("1", 1));
                sb.Append(GeneratePageLink("2", 2));
                sb.Append("<li class=\"break\">...</li>");
            }
            for (int i = start; i <= end; i++) {
                if (i == _currentPage) {
                    sb.AppendFormat("<li class=\"current\">{0}</li>" + Environment.NewLine, i);
                }
                else {
                    sb.Append(GeneratePageLink(i.ToString(), i));
                }
            }
            if (end < (pageCount - 3)) {
                sb.Append("<li class=\"break\">...</li>");
                sb.Append(GeneratePageLink((pageCount - 1).ToString(), pageCount - 1));
                sb.Append(GeneratePageLink(pageCount.ToString(), pageCount));
            }

            // Next
            if (_currentPage < pageCount) {
                sb.Append(GeneratePageLink("Next", (_currentPage + 1), false));
            }
            else {
                sb.Append("<li class=\"next-off\">Next</li>");
            }

            // End Ul tag
            sb.Append("</ul>" + Environment.NewLine);

            return sb.ToString();
        }

        private string GeneratePageLink(string linkText, int pageNumber) {
            var pageLinkValueDictionary = new RouteValueDictionary(_linkWithoutPageValuesDictionary);
            pageLinkValueDictionary.Add("page", pageNumber);

            foreach (var key in _viewContext.HttpContext.Request.QueryString.Keys) {
                if (!key.Equals("page") && !pageLinkValueDictionary.ContainsKey(key.ToString()))
                    pageLinkValueDictionary.Add(key.ToString(),
                                                _viewContext.HttpContext.Request.QueryString[key.ToString()]);
            }

            var virtualPathData = RouteTable.Routes.GetVirtualPath(_viewContext.RequestContext,
                                                                   pageLinkValueDictionary);

            if (virtualPathData != null) {
                string linkFormat = "<li><a href=\"{0}\" title=\"{1}\">{1}</a></li>" + Environment.NewLine;
                return String.Format(linkFormat, virtualPathData.VirtualPath, linkText);
            }
            return null;
        }

        private string GeneratePageLink(string linkText, int pageNumber, bool isPrev) {
            var pageLinkValueDictionary = new RouteValueDictionary(_linkWithoutPageValuesDictionary);
            pageLinkValueDictionary.Add("page", pageNumber);

            foreach (var key in _viewContext.HttpContext.Request.QueryString.Keys) {
                if (!key.Equals("page") && !pageLinkValueDictionary.ContainsKey(key.ToString()))
                    pageLinkValueDictionary.Add(key.ToString(),
                                                _viewContext.HttpContext.Request.QueryString[key.ToString()]);
            }

            var virtualPathData = RouteTable.Routes.GetVirtualPath(_viewContext.RequestContext,
                                                                   pageLinkValueDictionary);

            if (virtualPathData != null) {
                string linkFormat;

                if (isPrev)
                    linkFormat = "<li class=\"previous\"><a href=\"{0}\" title=\"{1}\">{1}</a></li>" +
                                 Environment.NewLine;
                else
                    linkFormat = "<li class=\"next\"><a href=\"{0}\" title=\"{1}\">{1}</a></li>" + Environment.NewLine;

                return String.Format(linkFormat, virtualPathData.VirtualPath, linkText);
            }
            return null;
        }
    }
}